#include "ChatGrpcClient.h"
#include "RedisManager.h"
#include "MysqlManager.h"

ChatConPool::ChatConPool(size_t poolSize, std::string host, std::string port)
	: m_poolSize(poolSize), m_host(host), m_port(port), m_b_stop(false) 
{
	for (size_t i = 0; i < m_poolSize; ++i) {
		std::shared_ptr<grpc::Channel> channel = grpc::CreateChannel(host + ":" + port, grpc::InsecureChannelCredentials());

		m_connections.push(message::ChatService::NewStub(channel));
	}
}

ChatConPool::~ChatConPool()
{
	std::lock_guard<std::mutex> lock(m_mutex);
	Close();
	while (!m_connections.empty()) {
		m_connections.pop();
	}
}

void ChatConPool::Close()
{
	m_b_stop = true;
	m_cond.notify_all();
}

std::unique_ptr<message::ChatService::Stub> ChatConPool::getConnection()
{
	std::unique_lock<std::mutex> lock(m_mutex);
	m_cond.wait(lock, [this]() {
		if (m_b_stop) {
			return true;
		}

		return !m_connections.empty();
	});

	if (m_b_stop) {
		return nullptr;
	}

	auto con = std::move(m_connections.front());
	m_connections.pop();

	return con;
}

void ChatConPool::returnConnection(std::unique_ptr<message::ChatService::Stub> con)
{
	std::lock_guard<std::mutex> lock(m_mutex);
	if (m_b_stop || !con) {
		return;
	}

	m_connections.push(std::move(con));
	m_cond.notify_one();
}

message::AddFriendRsp ChatGrpcClient::notifyAddFriend(std::string server_ip, const message::AddFriendReq& req)
{
	message::AddFriendRsp rsp;
	Defer defer([&rsp, &req]() {
		rsp.set_error(ErrorCodes::Success);
		rsp.set_applyuid(req.applyuid());
		rsp.set_touid(req.touid());
	});

	// 通过server_ip找到对应的连接池，从而找到通信的连接
	auto find_iter = m_pools.find(server_ip);
	if (find_iter == m_pools.end()) {
		return rsp;
	}

	auto& pool = find_iter->second;
	grpc::ClientContext context;
	auto stub = pool->getConnection();
	grpc::Status status = stub->NotifyAddFriend(&context, req, &rsp);
	Defer defercon([&stub, this, &pool]() {
		pool->returnConnection(std::move(stub));
	});

	if (!status.ok()) {
		rsp.set_error(ErrorCodes::RPCFailed);
		return rsp;
	}

	return rsp;
}

message::AuthFriendRsp ChatGrpcClient::notifyAuthFriend(std::string server_ip, const message::AuthFriendReq& req)
{
	message::AuthFriendRsp rsp;
	rsp.set_error(ErrorCodes::Success);

	Defer defer([&rsp, &req]() {
		rsp.set_fromuid(req.fromuid());
		rsp.set_touid(req.touid());
	});

	auto find_iter = m_pools.find(server_ip);
	if (find_iter == m_pools.end()) {
		return rsp;
	}

	auto& pool = find_iter->second;
	grpc::ClientContext context;
	auto stub = pool->getConnection();
	grpc::Status status = stub->NotifyAuthFriend(&context, req, &rsp);

	Defer defercon([&stub, this, &pool]() {
		pool->returnConnection(std::move(stub));
	});

	if (!status.ok()) {
		rsp.set_error(ErrorCodes::RPCFailed);
		return rsp;
	}

	return rsp;
}

bool ChatGrpcClient::getBaeInfo(std::string base_key, int uid, std::shared_ptr<UserInfo>& userinfo)
{
	// 优先查redis中查询用户信息
	std::string info_str = "";
	bool b_base = RedisManager::getInstance()->get(base_key, info_str);
	if (b_base) {
		Json::Reader reader;
		Json::Value root;
		reader.parse(info_str, root);
		userinfo->uid = root["uid"].asInt();
		userinfo->name = root["name"].asString();
		userinfo->pwd = root["pwd"].asString();
		userinfo->email = root["email"].asString();
		userinfo->nick = root["nick"].asString();
		userinfo->desc = root["desc"].asString();
		userinfo->sex = root["sex"].asInt();
		userinfo->icon = root["icon"].asString();
		std::cout << "user login uid is  " << userinfo->uid << " name  is "
				  << userinfo->name << " pwd is " << userinfo->pwd << " email is " << userinfo->email 
				  << std::endl;
	}
	else {
		// redis中没有则查询ysql
		std::shared_ptr<UserInfo> user_info = nullptr;
		user_info = MysqlManager::getInstance()->getUserInfo(uid);
		if (user_info == nullptr) {
			return false;
		}

		userinfo = user_info;

		// 将数据库内容写入redis缓存
		Json::Value redis_root;
		redis_root["uid"] = uid;
		redis_root["pwd"] = userinfo->pwd;
		redis_root["name"] = userinfo->name;
		redis_root["email"] = userinfo->email;
		redis_root["nick"] = userinfo->nick;
		redis_root["desc"] = userinfo->desc;
		redis_root["sex"] = userinfo->sex;
		redis_root["icon"] = userinfo->icon;
		RedisManager::getInstance()->set(base_key, redis_root.toStyledString());
	}
}

message::TextChatMsgRsp ChatGrpcClient::notifyTextChatMsg(std::string server_ip, const message::TextChatMsgReq& req, const Json::Value& rtvalue)
{
	message::TextChatMsgRsp rsp;
	rsp.set_error(ErrorCodes::Success);

	Defer defer([&rsp, &req]() {
		rsp.set_fromuid(req.fromuid());
		rsp.set_touid(req.touid());
		for (const auto& text_data : req.textmsgs()) {
			message::TextChatData* new_msg = rsp.add_textmsgs();
			new_msg->set_msgid(text_data.msgid());
			new_msg->set_msgcontent(text_data.msgcontent());
		}
	});

	auto find_iter = m_pools.find(server_ip);
	if (find_iter == m_pools.end()) {
		return rsp;
	}

	auto& pool = find_iter->second;
	grpc::ClientContext context;
	auto stub = pool->getConnection();
	grpc::Status status = stub->NotifyTextChatMsg(&context, req, &rsp);
	Defer defercon([&stub, this, &pool]() {
		pool->returnConnection(std::move(stub));
	});

	if (!status.ok()) {
		rsp.set_error(ErrorCodes::RPCFailed);
		return rsp;
	}

	return rsp;
}

ChatGrpcClient::ChatGrpcClient()
{
	auto& cfg = ConfigManager::getInstance();
	auto server_list = cfg["PeerServer"]["Servers"];

	std::vector<std::string> words;

	std::stringstream ss(server_list);
	std::string word;

	while (std::getline(ss, word, ',')) {
		words.push_back(word);
	}

	for (auto& word : words) {
		if (cfg[word]["Name"].empty()) {
			continue;
		}

		m_pools[cfg[word]["Name"]] = std::make_unique<ChatConPool>(5, cfg[word]["Host"], cfg[word]["Port"]);
	}
}
